﻿#ifndef __RENDERER_H__
#define __RENDERER_H__

#pragma once

#include "Material.h"
#include "RenderObject.h"
#include "RenderTarget.h"
#include "RendererParams.h"
#include "SceneGrpah.h"
#include "ShaderProgramManager.h"
#include "GeometryDataManager.h"
#include "MaterialManager.h"
#include "Texture.h"

// TODO

namespace Nezha
{

	class RenderQueue;
	struct RenderQueueEntry;
	class GraphicsDriver;

	/** base class of Renderer. */
	class NEZHA_ENGINE_ENTRY Renderer
	{
	public:
		Renderer(const _string& name);
		virtual ~Renderer();

		static Renderer* GetGlobal();

		float getFrameDelta() const
		{
			return mFrameDelta;
		}

		const MaterialPtr getDefaultMaterial() const;

		u8 getColorDepth() const;

		u16 getWidth() const;

		u16 getHeight() const;

		GraphicsDriver* getDriver();

		SceneGraph* getSceneGraph();

		GeometryDataManager* getGeometryDataManager();

		ShaderProgramManager* getShaderProgramManager();

		MaterialManager* getMaterialManager();

		TextureManager* getTextureManager();

		RTTManager* getRTTManager();

		/** ´´½¨Renderer¶ÔÏóÖ®ºó£¬µ÷ÓÃ¸Ã·½·¨½øÐÐ³õÊ¼»¯.×¢Òâ£¬¸Ã·½·¨Ö»¿Éµ÷ÓÃÒ»´Î
		*/
		void initializeOnce(u8 colBit, u16 w, u16 h);

		// 重置Scene Graph，之前scene graph的所有持有对象将被销毁。
		void resetSceneGraph(SceneGraph* sg);

		//void reset(u16 w, u16 h);

		/** É¾³ýrenderer¶ÔÏóÇ°±ØÐëµ÷ÓÃ */
		void uninitialize();

		//// TODO
		//virtual RenderTarget* createRenderTarget(u32 numTargets, TexFormat tf, u32 w, u32 h, int priority, bool autoMips,
		//	bool depthNStencil);

		// set current camera.
		void setCamera(Camera* cam);

		virtual bool beginFrame();

		virtual void clearFameBuffer(u8 clearFlag, const RGBA& col = RGBA::BLUE, float depth = 1.0f,
			u16 stencil = 0) = 0;

		void update(float dt);

		/**
		FIXME
			We consider only forward shading currently.
		*/
		void drawScene();

		virtual void endFrame();

		Camera* getCamera();

		RenderTarget* getFrameBuffer();

		void notifyCameraDestroy(Camera* cam);

		// for shader parser to create renderer params.
		RendererParam* createBuiltinParam(RendererParamsType rpt);

		RendererParam* getBuiltinParam(RendererParamsType rpt);

		void requestRenderQueue(RenderObject* ro);

		// FIXME
		RenderQueue* getRenderQueue(int flag);

		const RS_Blending& getDefaultBlending() const;
		const RS_CullOrder& getDefaultCullOrder() const;
		const RS_Depth& getDefaultDepth() const;
		const RS_PolygonOffset& getDefaultPolygonOffset() const;
		const RS_Stencil& getDefaultStencil() const;
		const RS_FillMode& getDefaultFillMode() const;
		const RS_ColorMask& getDefaultColorMask() const;

		const RS_Blending& getCurBlending() const;
		const RS_CullOrder& getCurCullOrder() const;
		const RS_Depth& getCurDepth() const;
		const RS_PolygonOffset& getCurPolygonOffset() const;
		const RS_Stencil& getCurStencil() const;
		const RS_FillMode& getCurFillMode() const;
		const RS_ColorMask& getCurColorMask() const;

		void setDefaultBlending(const RS_Blending& as);
		void setDefaultCullOrder(const RS_CullOrder& co);
		void setDefaultDepth(const RS_Depth& rd);
		void setDefaultPolygonOffset(const RS_PolygonOffset& po);
		void setDefaultStencil(const RS_Stencil& st);
		void setDefaultFillMode(const RS_FillMode& fm);
		void setDefaultColorMask(const RS_ColorMask& cm);

		/** 覆盖全局的fill mode, 用于全局现实线框或者点模式, 设置RSFM_IGNORE忽略全局的覆盖.
		@remarks
			implmention需要根据覆盖值设置每一个pass实际的fillmode.
		*/
		void overrideGlobalFillMode(const RS_FillMode& fm);

		void setBlending(const RS_Blending& as);
		void setCullOrder(const RS_CullOrder& co);
		void setDepth(const RS_Depth& rd);
		void setPolygonOffset(const RS_PolygonOffset& po);
		void setStencil(const RS_Stencil& st);
		void setFillMode(const RS_FillMode& fm);
		void setColorMask(const RS_ColorMask& cm);

		// override this for render states sorting.
		virtual u32 differBlending(const RS_Blending& as, const RS_Blending& asRhs);
		virtual u32 differCullOrder(const RS_CullOrder& co, const RS_CullOrder& cullRhs);
		virtual u32 differDepth(const RS_Depth& rd, const RS_Depth& depthRhs);
		virtual u32 differPolygonOffset(const RS_PolygonOffset& po, const RS_PolygonOffset& offRhs);
		virtual u32 differStencil(const RS_Stencil& st, const RS_Stencil& stenRhs);
		virtual u32 differFillMode(const RS_FillMode& fm, const RS_FillMode& fmRhs);
		virtual u32 differColorMask(const RS_ColorMask& cm, const RS_ColorMask& cmRhs);

NEZHA_INTERNAL:

		virtual void _draw(RenderQueueEntry* rqe);

		//////////////////////////////////////////////////////////////////////////
		/** the most raw way. */
		ShaderProgramPtr createShaderProgram(const _string& source);

		MaterialPtr createMaterial(const _string& name, ShaderProgramPtr& shader);

	protected:
		void setRenderMaterial(MaterialPtr& mat);

		void setShader(ShaderProgram* sp);

		bool setGeometry(GeometryDataBatch* gd, GeometryDataBatch* prevGd);

		void setShaderParameters(RenderPass* pass, RenderPass* prevPass = NULL);

		// ´´½¨MainFrameBuffer, Driver, GeometryDataManager, ShaderManager, TextureManager, RTTManager, etc
		virtual void createManagerImpls() = 0;

		// Ïú»ÙMainFrameBuffer, Driver, GeometryDataManager, ShaderManager, etc£¬ ²¢ÉèÖÃÆäÖ¸ÕëÎªNULL
		virtual void destroyManagerImpls() = 0;

		virtual void _update(float dt){}

		// set render states, the second param is the last state, if its pointer is not NULL
		// the impl should compare with previous state to avoid dumplicated state setting.
		virtual void _setBlending(const RS_Blending& as, const RS_Blending* lastAs = NULL) = 0;
		virtual void _setCullOrder(const RS_CullOrder& co, const RS_CullOrder* lastCull = NULL) = 0;
		virtual void _setDepth(const RS_Depth& rd, const RS_Depth* lastDepth = NULL) = 0;
		virtual void _setPolygonOffset(const RS_PolygonOffset& po, const RS_PolygonOffset* lastOffset = NULL) = 0;
		virtual void _setStencil(const RS_Stencil& st, const RS_Stencil* lastStencil = NULL) = 0;
		virtual void _setFillMode(const RS_FillMode& fm, const RS_FillMode* lastFM = NULL) = 0;
		virtual void _setColorMask(const RS_ColorMask& cm, const RS_ColorMask* lasCM = NULL) = 0;

		// 实现类需要覆盖该方法，并在末尾调用Renderer::setRenderTarget()
		virtual void setRenderTarget(RenderTarget* rt);

		// 实现类需要覆盖该方法，并在末尾调用Renderer::setViewport()
		virtual void setViewport(Viewport *vp);

		virtual void setSamplerImpl(u8 index, u8 totalNum, RenderPass* pass) = 0;

		virtual void setShaderImpl(ShaderProgram* sp) = 0;

		virtual bool makeInputAttribCompatible(GeometryDataBatch* gd, ShaderProgram* pg);

		virtual void setGeometryImpl(GeometryDataBatch* gd, GeometryDataBatch* prevGd) = 0;

		virtual void setShaderParameterImpl(ShaderParameter* sp, RenderPass* rp) = 0;

		virtual void drawSingle();

		void setLight(int idx, Light* lit);

		void restoreDefaultStates();

		// update renderer param when camera was changed.
		void updateRPByCurCam(RendererParamsType rpt);

		// update renderer param before rending a single object. 
		void updateRendererParams(RendererParam* rp);

		void drawScene(RenderTarget* rt, int flag);

		void drawDepth(int flag);

		void drawOpaqueLighting(int flag);

		//void preZPass();

		//void preShadowPass();

	private:
		static Renderer* msGlobalRenderer;

	protected:
		// REMARKS
		static void SetGlobal(Renderer* r);

		_string mName;

		GraphicsDriver* mDriver;

		MaterialPtr mDefaultMaterial;
		MaterialPtr mCurrentMaterial;

		// objects need be create by implemention.
		RenderTarget* mFrameBuffer;
		GeometryDataManager* mGeometryDataManager;
		ShaderProgramManager* mShaderProgramManager;
		MaterialManager* mMaterialManager;
		TextureManager* mTextureManager;
		RTTManager* mRTTManager;

		PArray<RenderTarget*> mRenderTargets;
		SceneGraph* mSceneGraph;

		RenderQueue* mOpaqueQueue;
		RenderQueue* mTransparentQueue;
		RenderQueueEntry* mLastRenderQueueEntry;
		RenderQueueEntry* mCurrentRenderQueueEntry;
		RenderTarget* mCurrentRenderTarget;
		Viewport* mCurrentViewport;
		Camera* mCurrentCam;

		ShaderProgram* mCurrentShader;
		GeometryDataBatch mLastBatch;
		GeometryDataBatch mCurrentBatch;

		// statistics
		// delta should be set by application.
		float mFrameDelta;
		float mFramesPerSecond;
		u32 mDrawCalls;
		u32 mTriangleCount;

		RendererParam* mBuiltinParams[MAX_RPT_NUM];

		// for forward rendering.
		Light* mCurrentLight;
		LightVector mCurrentLights;

		RS_Blending mDefBlendState;
		RS_CullOrder mDefCullOrder;
		RS_Depth mDefDepth;
		RS_FillMode mDefFillMode;
		RS_PolygonOffset mDefPolygonOffset;
		RS_Stencil mDefStencil;
		RS_ColorMask mDefColorMask;

		// current render states
		RS_Blending mCurBlendState;
		RS_CullOrder mCurCullOrder;
		RS_Depth mCurDepth;
		RS_FillMode mCurFillMode;
		RS_PolygonOffset mCurPolygonOffset;
		RS_Stencil mCurStencil;
		RS_ColorMask mCurColorMask;

		// global override
		RS_FillMode mGlobalFillMode;
		
		// lighting
		short mCurrentLightIndex;

		u16 mWidth;
		u16 mHeight;
		u8 mColorBit;

		bool mDefBlendStateOverride;
		bool mDefCullOrderOverride;
		bool mDefDepthOverride;
		bool mDefFillModeOverride;
		bool mDefOffsetOverride;
		bool mDefStencilOverride;
		bool mDefColorMaskOverride;
	};

	inline u8 Renderer::getColorDepth() const
	{
		return mColorBit;
	}

	inline u16 Renderer::getWidth() const
	{
		return mWidth;
	}

	inline u16 Renderer::getHeight() const
	{
		return mHeight;
	}

	inline GraphicsDriver* Renderer::getDriver()
	{
		return mDriver;
	}

	inline SceneGraph* Renderer::getSceneGraph()
	{
		return mSceneGraph;
	}

	inline GeometryDataManager* Renderer::getGeometryDataManager()
	{
		return mGeometryDataManager;
	}

	inline ShaderProgramManager* Renderer::getShaderProgramManager()
	{
		return mShaderProgramManager;
	}

	inline MaterialManager* Renderer::getMaterialManager()
	{
		return mMaterialManager;
	}

	inline TextureManager* Renderer::getTextureManager()
	{
		return mTextureManager;
	}

	inline RTTManager* Renderer::getRTTManager()
	{
		return mRTTManager;
	}

	inline const MaterialPtr Renderer::getDefaultMaterial() const
	{
		return mDefaultMaterial;
	}

	inline const RS_Blending& Renderer::getDefaultBlending() const
	{
		return mDefBlendState;
	}

	inline const RS_CullOrder& Renderer::getDefaultCullOrder() const
	{
		return mDefCullOrder;
	}

	inline const RS_Depth& Renderer::getDefaultDepth() const
	{
		return mDefDepth;
	}

	inline const RS_FillMode& Renderer::getDefaultFillMode() const
	{
		return mDefFillMode;
	}

	inline const RS_PolygonOffset& Renderer::getDefaultPolygonOffset() const
	{
		return mDefPolygonOffset;
	}

	inline const RS_Stencil& Renderer::getDefaultStencil() const
	{
		return mDefStencil;
	}

	inline const RS_ColorMask& Renderer::getDefaultColorMask() const
	{
		return mDefColorMask;
	}

	inline const RS_Blending& Renderer::getCurBlending() const
	{
		return mCurBlendState;
	}

	inline const RS_CullOrder& Renderer::getCurCullOrder() const
	{
		return mCurCullOrder;
	}

	inline const RS_Depth& Renderer::getCurDepth() const
	{
		return mCurDepth;
	}

	inline const RS_FillMode& Renderer::getCurFillMode() const
	{
		return mCurFillMode;
	}

	inline const RS_PolygonOffset& Renderer::getCurPolygonOffset() const
	{
		return mCurPolygonOffset;
	}

	inline const RS_Stencil& Renderer::getCurStencil() const
	{
		return mCurStencil;
	}

	inline const RS_ColorMask& Renderer::getCurColorMask() const
	{
		return mCurColorMask;
	}

	inline void Renderer::setDefaultBlending(const RS_Blending& as)
	{
		mDefBlendState = as;
		mDefBlendStateOverride = true;
	}

	inline void Renderer::setDefaultCullOrder(const RS_CullOrder& co)
	{
		mDefCullOrder = co;
		mDefCullOrderOverride = true;
	}

	inline void Renderer::setDefaultDepth(const RS_Depth& rd)
	{
		mDefDepth = rd;
		mDefDepthOverride = true;
	}

	inline void Renderer::setDefaultFillMode(const RS_FillMode& fm)
	{
		mDefFillMode = fm;
		mDefFillModeOverride = true;
	}

	inline void Renderer::setDefaultPolygonOffset(const RS_PolygonOffset& po)
	{
		mDefPolygonOffset = po;
		mDefOffsetOverride = true;
	}

	inline void Renderer::setDefaultStencil(const RS_Stencil& st)
	{
		mDefStencil = st;
		mDefStencilOverride = true;
	}

	inline void Renderer::setDefaultColorMask(const RS_ColorMask& cm)
	{
		mDefColorMask = cm;
		mDefColorMaskOverride = true;
	}

	inline void Renderer::overrideGlobalFillMode(const RS_FillMode& fm)
	{
		mGlobalFillMode = fm;
	}

}//end namespace Nezha

#endif //end __RENDERER_H__